/*
 * File      : rz.c
 * the core functions of implementing zmodem protocol
 * Change Logs:
 * Date           Author       Notes
 * 2011-03-29     itspy
 */

#include <rtthread.h>
#include <finsh.h>
#include <shell.h>
#include <rtdef.h>
#include <dfs.h>
#include <dfs_file.h>
#include <dfs_posix.h>
#include <stdio.h>
#include "zdef.h"

char ZF0_CMD;		             /* file conversion request */
char ZF1_CMD;	 	             /* file management request */
char ZF2_CMD;		             /* file transport request */
char ZF3_CMD;
rt_uint8_t   Rxframeind;		 /* ZBIN ZBIN32, or ZHEX type of frame */
rt_uint16_t  Rxcount;		     /* received count*/
char header_type;	             /* header type */
rt_uint8_t   rx_header[4];	     /* received header */
rt_uint8_t   tx_header[4];	     /* transmitted header */
rt_uint32_t  Rxpos;		         /* received file position */
rt_uint32_t  Txpos;		         /* transmitted file position */
rt_uint8_t   Txfcs32;		     /* TURE means send binary frames with 32 bit FCS */
rt_uint8_t   TxCRC;		         /* controls 32 bit CRC being sent */
rt_uint8_t   RxCRC;		         /* indicates/controls 32 bit CRC being received */
/* 0 == CRC16,  1 == CRC32,  2 == CRC32 + RLE */
char Attn[ZATTNLEN + 1];	       /* attention string rx sends to tx on err */

void zinit_parameter(void);
void zsend_bin_header(rt_uint8_t type, rt_uint8_t* hdr);
void zsend_hex_header(rt_uint8_t type, rt_uint8_t* hdr);
void zsend_bin_data(rt_uint8_t* buf, rt_int16_t len, rt_uint8_t frameend);
static rt_int16_t zrec_data16(rt_uint8_t* buf, rt_uint16_t len);
static rt_int16_t zrec_data32(rt_uint8_t* buf, rt_int16_t len);
static rt_int16_t zrec_data32r(rt_uint8_t* buf, rt_int16_t len);
rt_int16_t zget_data(rt_uint8_t* buf, rt_uint16_t len);
rt_int16_t zget_header(rt_uint8_t* hdr);
static rt_int16_t zget_bin_header(rt_uint8_t* hdr);
static rt_int16_t zget_bin_fcs(rt_uint8_t* hdr);
rt_int16_t zget_hex_header(rt_uint8_t* hdr);
static void zsend_ascii(rt_uint8_t c);
void zsend_zdle_char(rt_uint16_t ch);
static rt_int16_t zget_hex(void);
rt_int16_t zread_byte(void);
rt_int16_t zxor_read(void);
void zput_pos(rt_uint32_t pos);
void zget_pos(rt_uint32_t pos);




void zinit_parameter(void)
{
	rt_uint8_t i;

	ZF0_CMD  = CANFC32 | CANFDX | CANOVIO;		/*  not chose CANFC32,CANRLE,although it have been supported */
	ZF1_CMD  = 0;							    /* fix header length,not support CANVHDR */
	ZF2_CMD  = 0;
	ZF3_CMD  = 0;
	Rxframeind = 0;
	header_type   = 0;
	Rxcount  = 0;

	for(i = 0; i < 4; i++) rx_header[i] = tx_header[i] = 0;

	Rxpos    = Txpos = 0;
	RxCRC    = 0;
	Txfcs32  = 0;

	return ;
}

/* send binary header */
void zsend_bin_header(rt_uint8_t type, rt_uint8_t* hdr)
{
	rt_uint8_t i;
	rt_uint32_t crc;

	zsend_byte(ZPAD);
	zsend_byte(ZDLE);
	TxCRC = Txfcs32;

	if(TxCRC == 0) {
		zsend_byte(ZBIN);
		zsend_zdle_char(type);
		/* add 16bits crc */
		crc = 0L;
		crc = updcrc16(type, 0);

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(*hdr);
			crc = updcrc16((0377 & *hdr++), crc);
		}

		crc = updcrc16(0, updcrc16(0, crc));
		zsend_zdle_char(((int)(crc >> 8)));
		zsend_zdle_char(crc);
	} else if(TxCRC == 1) {
		zsend_byte(ZBIN32);
		zsend_zdle_char(type);
		/* add 32bits crc */
		crc = 0xffffffffL;
		crc = updcrc32(type, crc);

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(*hdr);
			crc = updcrc32((0377 & *hdr++), crc);
		}

		crc = ~crc;

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(crc);
			crc >>= 8;
		}
	} else if(TxCRC == 2) {
		zsend_byte(ZBINR32);
		zsend_zdle_char(type);
		/* add 32bits crc */
		crc = 0xffffffffL;
		crc = updcrc32(type, crc);

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(*hdr);
			crc = updcrc32((0377 & *hdr++), crc);
		}

		crc = ~crc;

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(crc);
			crc >>= 8;
		}
	}

	return;
}

/* send hex header */
void zsend_hex_header(rt_uint8_t type, rt_uint8_t* hdr)
{
	rt_uint8_t i;
	rt_uint16_t crc;

	zsend_line(ZPAD);
	zsend_line(ZPAD);
	zsend_line(ZDLE);
	zsend_line(ZHEX);
	zsend_ascii(type);
	crc = updcrc16(type, 0);

	for(i = 0; i < 4; i++) {
		zsend_ascii(*hdr);
		crc = updcrc16((0377 & *hdr++), crc);
	}

	crc = updcrc16(0, updcrc16(0, crc));
	zsend_ascii(crc >> 8);
	zsend_ascii(crc);
	/* send display control cmd */
	zsend_line(015);
	zsend_line(0212);

	if(type != ZFIN && type != ZACK)
		zsend_line(021);

	TxCRC = 0;               /* clear tx crc type */

	return;
}

/* send binary data,with frameend */
void zsend_bin_data(rt_uint8_t* buf, rt_int16_t len, rt_uint8_t frameend)
{
	rt_int16_t i, c, tmp;
	rt_uint32_t crc;

	if(TxCRC == 0) {        /* send binary data with 16bits crc check */
		crc = 0x0L;

		for(i = 0; i < len; i++) {
			zsend_zdle_char(*buf);
			crc = updcrc16((0377 & *buf++), crc);
		}

		zsend_byte(ZDLE);
		zsend_byte(frameend);
		crc = updcrc16(frameend, crc);
		crc = updcrc16(0, updcrc16(0, crc));
		zsend_zdle_char(crc >> 8);
		zsend_zdle_char(crc);
	} else if(TxCRC == 1) { /* send binary data with 32 bits crc check */
		crc = 0xffffffffL;

		for(i = 0; i < len; i++) {
			c = *buf++ & 0377;
			zsend_zdle_char(c);
			crc = updcrc32(c, crc);
		}

		zsend_byte(ZDLE);
		zsend_byte(frameend);
		crc = updcrc32(frameend, crc);
		crc = ~crc;

		for(i = 0; i < 4; i++) {
			zsend_zdle_char((int)crc);
			crc >>= 8;
		}
	} else if(TxCRC == 2) { /* send binary data with 32bits crc check,RLE encode */
		crc = 0xffffffffL;
		tmp = *buf++ & 0377;

		for(i = 0; --len >= 0; ++buf) {
			if((c = *buf & 0377) == tmp && i < 126 && len > 0) {
				++i;
				continue;
			}

			if(i == 0) {
				zsend_zdle_char(tmp);
				crc = updcrc32(tmp, crc);

				if(tmp == ZRESC) {
					zsend_zdle_char(0100);
					crc = updcrc32(0100, crc);
				}

				tmp = c;
			} else if(i == 1) {
				if(tmp != ZRESC) {
					zsend_zdle_char(tmp);
					zsend_zdle_char(tmp);
					crc = updcrc32(tmp, crc);
					crc = updcrc32(tmp, crc);
					i = 0;
					tmp = c;
				}

			} else {
				zsend_zdle_char(ZRESC);
				crc = updcrc32(ZRESC, crc);

				if(tmp == 040 && i < 34) {
					i += 036;
					zsend_zdle_char(i);
					crc = updcrc32(i, crc);
				} else {
					i += 0101;
					zsend_zdle_char(i);
					crc = updcrc32(i, crc);
					zsend_zdle_char(tmp);
					crc = updcrc32(tmp, crc);
				}

				i = 0;
				tmp = c;
			}
		}

		zsend_byte(ZDLE);
		zsend_byte(frameend);
		crc = updcrc32(frameend, crc);
		crc = ~crc;

		for(i = 0; i < 4; i++) {
			zsend_zdle_char(crc);
			crc >>= 8;
		}
	}

	if(frameend == ZCRCW)
		zsend_byte(XON);

	return;
}

/* receive data,with 16bits CRC check */
static rt_int16_t zrec_data16(rt_uint8_t* buf, rt_uint16_t len)
{
	rt_int16_t c, crc_cnt;
	rt_uint16_t crc;
	rt_err_t res = -RT_ERROR;
	rt_uint8_t* p, flag = 0;

	p = buf;
	crc_cnt = 0;
	crc = 0L;
	Rxcount = 0;

	while(buf <= p + len) {
		if((res = zread_byte()) & ~0377) {
			if(res == GOTCRCE || res == GOTCRCG ||
			        res == GOTCRCQ || res == GOTCRCW) {
				c = res;
				c = res;
				crc = updcrc16(res & 0377, crc);
				flag = 1;
				continue;
			} else if(res == GOTCAN)  return ZCAN;
			else if(res == TIMEOUT) return TIMEOUT;
			else return res;

		} else {
			if(flag) {
				crc = updcrc16(res, crc);
				crc_cnt++;

				if(crc_cnt < 2) continue;

				if((crc & 0xffff)) {
#ifdef ZDEBUG
					rt_kprintf("error code: CRC16 error \r\n");
#endif
					return -RT_ERROR;
				}

				return c;
			} else {
				*buf++ = res;
				Rxcount++;
				crc = updcrc16(res, crc);
			}
		}
	}

	return -RT_ERROR;
}

/* receive data,with 32bits CRC check */
static rt_int16_t zrec_data32(rt_uint8_t* buf, rt_int16_t len)
{
	rt_int16_t c, crc_cnt;
	rt_uint32_t crc;
	rt_err_t res = -RT_ERROR;
	rt_uint8_t* p, flag = 0;

	crc_cnt = 0;
	crc = 0xffffffffL;
	Rxcount = 0;

	while(buf <= p + len) {
		if((res = zread_byte()) & ~0377) {
			if(res == GOTCRCE || res == GOTCRCG ||
			        res == GOTCRCQ || res == GOTCRCW) {
				c = res;
				crc = updcrc32(res & 0377, crc);
				flag = 1;
				continue;
			} else if(res == GOTCAN)  return ZCAN;
			else if(res == TIMEOUT) return TIMEOUT;
			else return res;

		} else {
			if(flag) {
				crc = updcrc32(res, crc);
				crc_cnt++;

				if(crc_cnt < 4) continue;

				if((crc & 0xDEBB20E3)) {
#ifdef ZDEBUG
					rt_kprintf("error code: CRC32 error \r\n");
#endif
					return -RT_ERROR;
				}

				return c;
			} else {
				*buf++ = res;
				Rxcount++;
				crc = updcrc32(res, crc);
			}
		}
	}

	return -RT_ERROR;
}
/* receive data,with RLE encoded,32bits CRC check */
static rt_int16_t zrec_data32r(rt_uint8_t* buf, rt_int16_t len)
{
	rt_int16_t c, crc_cnt;
	rt_uint32_t crc;
	rt_err_t res = -RT_ERROR;
	rt_uint8_t* p, flag = 0;

	crc_cnt = 0;
	crc = 0xffffffffL;
	Rxcount = 0;
	p = buf;

	while(buf <= p + len) {
		if((res = zread_byte()) & ~0377) {
			if(res == GOTCRCE || res == GOTCRCG ||
			        res == GOTCRCQ || res == GOTCRCW) {
				c = res;
				crc = updcrc32(res & 0377, crc);
				flag = 1;
				continue;
			} else if(res == GOTCAN)  return ZCAN;
			else if(res == TIMEOUT) return TIMEOUT;
			else return res;

		} else {
			if(flag) {
				crc = updcrc32(res, crc);
				crc_cnt++;

				if(crc_cnt < 4) continue;

				if((crc & 0xDEBB20E3)) {
#ifdef ZDEBUG
					rt_kprintf("error code: CRC32 error \r\n");
#endif
					return -RT_ERROR;
				}

				return c;
			} else {
				crc = updcrc32(res, crc);

				switch(c) {
					case 0:
						if(res == ZRESC) {
							c = -1;
							continue;
						}

						*buf++ = res;
						Rxcount++;
						continue;

					case -1:
						if(res >= 040 && res < 0100) {
							c = res - 035;
							res = 040;
							goto spaces;
						}

						if(res == 0100) {
							c = 0;
							*buf++ = ZRESC;
							Rxcount++;
							continue;
						}

						c = res;
						continue;

					default:
						c -= 0100;

						if(c < 1)
							goto end;

spaces:

						if((buf + c) > p + len)
							goto end;

						while(--res >= 0) {
							*buf++ = res;
							Rxcount++;
						}

						c = 0;
						continue;
				}
			}
		}	// if -else

	}

end:
	return -RT_ERROR;
}
rt_int16_t zget_data(rt_uint8_t* buf, rt_uint16_t len)
{
	rt_int16_t res = -RT_ERROR;

	if(RxCRC == 0) {
		res = zrec_data16(buf, len);
	} else if(RxCRC == 1) {
		res = zrec_data32(buf, len);
	} else if(RxCRC == 2) {
		res = zrec_data32r(buf, len);
	}

	return res;
}
/* get type and cmd of header, fix lenght */
rt_int16_t zget_header(rt_uint8_t* hdr)
{
	rt_int16_t c, prev_char;
	rt_uint32_t bit;
	rt_uint16_t get_can, step_out;

	bit = get_device_baud();	             /* get console baud rate */
	Rxframeind = header_type = 0;
	step_out = 0;
	prev_char = 0xff;

	for(;;) {
		c = zread_line(100);

		switch(c) {
			case 021:
			case 0221:
				if(prev_char == CAN)   break;

				if(prev_char == ZCRCW)  goto start_again;

				break;

			case RCDO:
				goto end;

			case TIMEOUT:
				if(prev_char == CAN) break;

				if(prev_char == ZCRCW) {
					c = -RT_ERROR;
					goto end;
				}

				goto end;

			case ZCRCW:
				if(prev_char == CAN) goto start_again;

				break;

			case CAN:
get_can:
				if(++get_can > 5) {
					c = ZCAN;
					goto end;
				}

				break;

			case ZPAD:
				if(prev_char == CAN)   break;

				if(prev_char == ZCRCW) goto start_again;

				step_out = 1;
				break;

			default:
				if(prev_char == CAN)   break;

				if(prev_char == ZCRCW) goto start_again;

start_again:

				if(--bit == 0) {
					c = GCOUNT;
					goto end;
				}

				get_can = 0;
				break;
		}

		prev_char = c;

		if(step_out) break;     /* exit loop */
	}

	step_out = get_can = 0;

	for(;;) {
		c = zxor_read();

		switch(c) {
			case ZPAD:
				break;

			case RCDO:
			case TIMEOUT:
				goto end;

			case ZDLE:
				step_out = 1;
				break;

			default:
				goto start_again;
		}

		if(step_out) break;
	}

	Rxframeind = c = zxor_read();

	switch(c) {
		case ZBIN32:
			RxCRC = 1;
			c = zget_bin_fcs(hdr);
			break;

		case ZBINR32:
			RxCRC = 2;
			c = zget_bin_fcs(hdr);
			break;

		case ZBIN:
			RxCRC = 0;
			c = zget_bin_header(hdr);
			break;

		case ZHEX:
			RxCRC = 0;
			c = zget_hex_header(hdr);
			break;

		case CAN:
			goto get_can;

		case RCDO:
		case TIMEOUT:
			goto end;

		default:
			goto start_again;
	}

end:
	return c;
}

/* receive a binary header */
static rt_int16_t zget_bin_header(rt_uint8_t* hdr)
{
	rt_int16_t res, i;
	rt_uint16_t crc;

	if((res = zread_byte()) & ~0377)
		return res;

	header_type = res;
	crc = updcrc16(res, 0);

	for(i = 0; i < 4; i++) {
		if((res = zread_byte()) & ~0377)
			return res;

		crc = updcrc16(res, crc);
		*hdr++ = res;
	}

	if((res = zread_byte()) & ~0377)
		return res;

	crc = updcrc16(res, crc);

	if((res = zread_byte()) & ~0377)
		return res;

	crc = updcrc16(res, crc);

	if(crc & 0xFFFF) {
		rt_kprintf("CRC error\n");
		return -RT_ERROR;
	}

	return header_type;
}

/* receive a binary header,with 32bits FCS */
static rt_int16_t zget_bin_fcs(rt_uint8_t* hdr)
{
	rt_int16_t res, i;
	rt_uint32_t crc;

	if((res = zread_byte()) & ~0377)
		return res;

	header_type = res;
	crc = 0xFFFFFFFFL;
	crc = updcrc32(res, crc);

	for(i = 0; i < 4; i++) { /* 4headers */
		if((res = zread_byte()) & ~0377)
			return res;

		crc = updcrc32(res, crc);
		*hdr++ = res;

	}

	for(i = 0; i < 4; i++) {	/* 4bytes crc */
		if((res = zread_byte()) & ~0377)
			return res;

		crc = updcrc32(res, crc);

	}

	if(crc != 0xDEBB20E3) {
#ifdef ZDEBUG
		rt_kprintf("CRC error\n");
#endif
		return -RT_ERROR;
	}

	return header_type;
}


/* receive a hex style header (type and position) */
rt_int16_t zget_hex_header(rt_uint8_t* hdr)
{
	rt_int16_t res, i;
	rt_uint16_t crc;

	if((res = zget_hex()) < 0)
		return res;

	header_type = res;
	crc = updcrc16(res, 0);

	for(i = 0; i < 4; i++) {
		if((res = zget_hex()) < 0)
			return res;

		crc = updcrc16(res, crc);
		*hdr++ = res;
	}

	if((res = zget_hex()) < 0)
		return res;

	crc = updcrc16(res, crc);

	if((res = zget_hex()) < 0)
		return res;

	crc = updcrc16(res, crc);

	if(crc & 0xFFFF) {
#ifdef ZDEBUG
		rt_kprintf("error code : CRC error\r\n");
#endif
		return -RT_ERROR;
	}

	res = zread_line(100);

	if(res < 0)
		return res;

	res = zread_line(100);

	if(res < 0)
		return res;

	return header_type;
}

/* convert to ascii */
static void zsend_ascii(rt_uint8_t c)
{
	const char hex[] = "0123456789abcdef";

	zsend_line(hex[(c & 0xF0) >> 4]);
	zsend_line(hex[(c) & 0xF]);

	return;
}

/*
 * aend character c with ZMODEM escape sequence encoding.
 */
void zsend_zdle_char(rt_uint16_t ch)
{
	rt_uint16_t	res;

	res = ch & 0377;

	switch(res) {
		case 0377:
			zsend_byte(res);
			break;

		case ZDLE:
			zsend_byte(ZDLE);
			res ^= 0100;
			zsend_byte(res);
			break;

		case 021:
		case 023:
		case 0221:
		case 0223:
			zsend_byte(ZDLE);
			res ^= 0100;
			zsend_byte(res);
			break;

		default:
			zsend_byte(res);
	}
}

/* decode two lower case hex digits into an 8 bit byte value */
static rt_int16_t zget_hex(void)
{
	rt_int16_t res, n;

	if((res = zxor_read()) < 0)
		return res;

	n = res - '0';

	if(n > 9)
		n -= ('a' - ':');

	if(n & ~0x0f)
		return -RT_ERROR;

	if((res = zxor_read()) < 0)
		return res;

	res -= '0';

	if(res > 9)
		res -= ('a' - ':');

	if(res & ~0x0f)
		return -RT_ERROR;

	res += (n << 4);

	return res;
}


/*
 * read a byte, checking for ZMODEM escape encoding
 *  including CAN*5 which represents a quick abort
 */
rt_int16_t zread_byte(void)
{
	register int res;

again:

	/* Quick check for non control characters */
	if((res = zread_line(100)) & 0140)
		return res;

	switch(res) {
		case ZDLE:
			break;

		case 023:
		case 0223:
		case 021:
		case 0221:
			goto again;

		default:
			return res;
	}

again2:

	if((res = zread_line(100)) < 0)
		return res;

	if(res == CAN && (res = zread_line(100)) < 0)
		return res;

	if(res == CAN && (res = zread_line(100)) < 0)
		return res;

	if(res == CAN && (res = zread_line(100)) < 0)
		return res;

	switch(res) {
		case CAN:
			return GOTCAN;

		case ZCRCE:
		case ZCRCG:
		case ZCRCQ:
		case ZCRCW:
			return (res | GOTOR);

		case ZRUB0:
			return 0177;

		case ZRUB1:
			return 0377;

		case 023:
		case 0223:
		case 021:
		case 0221:
			goto again2;

		default:
			if((res & 0140) ==  0100)
				return (res ^ 0100);

			break;
	}

	return -RT_ERROR;
}

/*
 * @read a character from the modem line with timeout.
 * @eat parity, XON and XOFF characters.
 */
rt_int16_t zxor_read(void)
{
	rt_int16_t res;

	for(;;) {
		if((res = zread_line(100)) < 0)
			return res;

		switch(res &= 0177) {
			case XON:
			case XOFF:
				continue;

			case '\r':
			case '\n':
			case ZDLE:
			default:
				return res;
		}
	}

}

/* put file posistion into the header*/
void zput_pos(rt_uint32_t pos)
{
	tx_header[ZP0] = pos;
	tx_header[ZP1] = pos >> 8;
	tx_header[ZP2] = pos >> 16;
	tx_header[ZP3] = pos >> 24;

	return;
}

/* Recover a long integer from a header */
void zget_pos(rt_uint32_t pos)
{
	Rxpos = (rx_header[ZP3] & 0377);
	Rxpos = (Rxpos << 8) | (rx_header[ZP2] & 0377);
	Rxpos = (Rxpos << 8) | (rx_header[ZP1] & 0377);
	Rxpos = (Rxpos << 8) | (rx_header[ZP0] & 0377);

	return;
}

/* end of zcore.c */
